#include "libutilsconfig.h"
#include "tCallStack.h"
#include <iostream>
#include <sstream>
#ifdef WIN32
#pragma warning(disable:4786)
#endif


static thread_id thread_self()
{
#ifdef _MSC_VER
	return (unsigned)GetCurrentThread();
#else
	return pthread_self();
#endif
}

LIB_NS_BEGIN

std::map<thread_id, TCallStack::Context> TCallStack::s_stack;
std::map<thread_id, TCallStack::Context> TCallStack::s_error;
TMutex TCallStack::s_mutex;
int32 TCallStack::Ignore::s_count = 0;

TCallStack::TCallStack(const tstring& name, const tstring& file, int32 line)
{
	Locker locker(s_mutex);

#if TERMINATE_IN_STD
    std::set_terminate(FIX::CallStack::terminate);
#else
    set_terminate(LIB_NS::TCallStack::Terminate);
#endif

	Context& c = GetContext();
	if( !c.ignore )
	{
		c.push(Method(name, file, line));
		GetErrorContext() = c;
	}
}

TCallStack::~TCallStack()
{
	Locker locker(s_mutex);
	Context& c = GetContext();
	if( !c.ignore )
		c.pop();
}

void TCallStack::Output()
{
	Locker locker(s_mutex);
	std::map<thread_id, TCallStack::Context>::iterator i;

	for( i = s_error.begin(); i != s_error.end(); ++i )
	{
		Context& c = i->second;
		if(c.size())
			std::cerr << _T( "thread(" ) << i->first << _T( "):" ) << c.exception.c_str() << std::endl;
		while(c.size())
		{
		std::cerr << _T("  at ") << c.top() << std::endl;
		c.pop();
		}
	}
}

void TCallStack::Ignore(bool value)
{
	Locker locker(s_mutex);
	GetContext().ignore = value;
}

void TCallStack::Caught(std::exception& e )
{
	Locker locker(s_mutex);
	Context& c = GetErrorContext();
	if( c.ignore ) return;
	c.Caught(e);
}

void TCallStack::Caught()
{
	Locker locker(s_mutex);
	Context& c = GetErrorContext();
	if( c.ignore ) return;
	c.Caught();
}

TCallStack::Context& TCallStack::GetContext()
{
	Locker locker(s_mutex);
	return s_stack[thread_self()];
}

TCallStack::Context& TCallStack::GetErrorContext()
{
	Locker locker(s_mutex);
	return s_error[thread_self()];
}

void TCallStack::Terminate()
{
	Locker locker(s_mutex);
	TCallStack::Output();
	abort();
}

void TCallStack::Context::Caught( std::exception& e )
{
#if TYPEINFO_IN_STD
	const std::type_info&
#else
	const type_info&
#endif
	type = typeid(e);

	std::stringstream s;
	s << type.name() << _T(": ") << std::string(e.what());
	//exception = std::exception(s.str().c_str());//TODO CHH???
}

void TCallStack::Context::Caught()
{
	exception = _T("unknown exception");
}

TCallStack::Context::Context() : ignore(false)
{
}

TCallStack::Method::Method()
{
}

TCallStack::Method::Method( const tstring& n, const tstring& f, int32 l ) 
	: name(n), file(f), line(l)
{
}

TCallStack::Method& TCallStack::Method::operator=( const TCallStack::Method& rhs )
{
	name = rhs.name;
	file = rhs.file;
	line = rhs.line;
	return *this;
}

std::ostream& operator<<( std::ostream& ostream, 
	const TCallStack::Method& method )
{
	return ostream
		<< method.name.c_str()
		<< "(" << method.file.c_str() << ":" << method.line << ")";
}

TCallStack::Ignore::Ignore()
{
	s_count++;
	TCallStack::Ignore(true);
}

TCallStack::Ignore::~Ignore()
{
	s_count--;
	if( s_count == 0 )
		TCallStack::Ignore(false);
}

LIB_NS_END
